home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Carousel Volume 2 #1
/
carousel.iso
/
mactosh
/
patches
/
thinkc.sit
/
Think ƒ
/
printf-3.c
< prev
next >
Wrap
Text File
|
1989-02-25
|
20KB
|
893 lines
/*
printf formatter for LightspeedC
(C) Copyright 1985, 1986. THINK Technologies, Inc. All rights reserved.
pdg - 6/11/86 - revised
*/
/* comment this out if you don't need floating point numeric support */
#define _FORMAT_FP_
#ifndef _stdioh_
#include "stdio.h"
#endif
#ifndef _MacTypes_
#include "MacTypes.h"
#endif
#ifndef _saneh_
#include "sane.h"
#endif
#ifndef _setjmph_
#include "setjmp.h"
#endif
#ifndef _math_
#include "Math.h"
#endif
#ifdef _MC68881_
/* conversion from 68881 to SANE extended type */
static void x96tox80(x96, x80)
register Extended96 *x96;
register Extended80 *x80;
{
(*x80).exponent = (*x96).exponent;
(*x80).mantissa = (*x96).mantissa;
}
#define _tox80() x96tox80(doublep, &x80);
#define _ToDecimal() fp68k(&_decform_, &x80, &_decimal_, FFEXT+FOB2D)
#else
#define _tox80()
#define _ToDecimal() fp68k(&_decform_, doublep, &_decimal_, FFEXT+FOB2D)
#endif
/* it turns out that defining min, max, and isdigit as follows works pretty
well and drags in less code from other modules */
#undef min
#undef max
#undef isdigit
#define min(a,b) ( (a<b) ? (a) : (b) )
#define max(a,b) ( (a>b) ? (a) : (b) )
#define isdigit(c) ( (c<='9') && (c>='0') )
#define BLANKS 0
void (*_output)(); /* global pointer to function for output */
int _num_count; /* number of characters transmitted */
jmp_buf env; /* global error return mechanism */
static char _hex_case; /* used for x X case */
static char fillchar; /* space or '0' depending on mode */
static int base; /* global to store base under conversion */
#line 0 check_for_three()
static char *check_for_three(ptr,tptr)
char *ptr,*tptr;
{
int l;
/* if length of exponent is 3 or more then return */
if ((l =len_of_str(ptr))>2) return(tptr);
/* This implementation normally makes the exponent 3 bytes wide */
*tptr++ = '0'; /* make default width = 2 */
/* if 2 byte default width is desired then comment out the
following line of code */
if (l == 1) *tptr++ = '0'; /* make default width = 3 */
return(tptr);
}
#line 0 dumpbuffer()
static void dumpbuffer(tbufptr) /* write buffer to output stream */
register char *tbufptr;
{
register void (*outputx)() = _output;
if (*tbufptr == '@') tbufptr++;
while (*tbufptr)
(*outputx)(*tbufptr++);
}
#line 0 dopadding()
static void dopadding(tempbuffer,left_justify,zero_fill,width)
register char *tempbuffer;
register Boolean left_justify,zero_fill;
register int width;
{
register int length;
register void (*outputx)() = _output;
if ((length = len_of_str(tempbuffer))<width)
{
width -= length;
if (left_justify == false)
{
if ((*tempbuffer == ' ')||
(((*tempbuffer == '+')||(*tempbuffer == '-'))&&zero_fill) )
{
(*outputx)(*tempbuffer);
*tempbuffer = '@';
}
while (--width >= 0)
(*outputx)(zero_fill?'0':' ');
dumpbuffer(tempbuffer);
}
else
{
dumpbuffer(tempbuffer);
while (--width >= 0)
(*outputx)(' ');
}
}
else /* pdg - 7/23/86 missing else! */
dumpbuffer(tempbuffer);
}
/* pdg - 6/10/86 - revise for efficiency */
#line 0 len_of_str()
static int len_of_str(s)
register char *s;
{
register char *s0 = s;
while (*s) s++;
return(s-s0);
}
#line 0 padd()
static void padd(zero_fill,count)
Boolean zero_fill;
register int count;
{
while (--count >= 0)
(*_output)(((zero_fill)?'0':' '));
}
#line 0 output_number()
static void output_number(value)
unsigned long value;
{
unsigned long quotient;
register int remainder;
if ((quotient = value / base) != 0) output_number(quotient);
remainder = (value % base);
(*_output)(((remainder<10) ? remainder + '0' :
remainder + _hex_case));
}
#line 0 length of number()
static int length_of_number(number)
register unsigned long number;
{
register int places = 1;
while (number /= base) places++;
return (places);
}
#ifdef _FORMAT_FP_
char *PtoCstr(); /* need proper typing of return value */
#line 0 cvtf2string()
static char *cvtf2string(doublep, style, digits, floatbufferp)
double *doublep;
char style;
short digits;
char *floatbufferp;
{
DecForm _decform_; /* information record for conversion */
Decimal _decimal_; /* intermediate record */
#ifdef _MC68881_
Extended80 x80;
#endif
/* convert double to Extended80 (as defined in Math881.h) */
_tox80();
/* convert binary to decimal record */
_decform_.style = style;
_decform_.digits = digits;
_ToDecimal();
/* decimal record to string */
_decform_.style= style;
_decform_.digits = digits;
Dec2Str(_decform_, &_decimal_, floatbufferp);
/* convert to C string from Pascal string used by SANE */
return (PtoCstr(floatbufferp));
}
#endif _FORMAT_FP_
#line 0 _format()
/*----------------------------------------------------------------------------
"_format" is the internal routine to do the conversion process. This routine
is passed a pointer to the pointer to the format string. By scanning the
format string until a format code is encountered, information can be extrac-
ted to determine the what argument was present on the stack and processing
can be handled. Note that the following stack layout applies after the call
to _format (standard C calling conventions):
HI memory
|---------------------|
| last argument | last argument for format
|---------------------|
.
.
|---------------------|
|--->---| first argument | points to format string
| |---------------------|
| | return address |
| |---------------------|
|---<---| argument to _format | points to pointer to format
|---------------------|
| return address |
|---------------------|
LO memory
The _format routine sets "format" to point to the format string and scans
over it. By incrementing the parameter "ptr" (passed to _format) the
first argument for substitution in the format string can be accessed.
How the arguments are interpreted is derived from the format string.
A pointer to a function "*_output" is used to stuff processed characters
to their destination.
-----------------------------------------------------------------------------*/
int _format(fmt,var_stuff)
char **fmt; /* pointer to pointer to format string */
Boolean var_stuff;
{
Boolean left_justify; /* flag to indicate left justiccation */
Boolean do_precision; /* flag limited precision */
Boolean its_a_long; /* flag to indicate long integer */
Boolean zero_fill; /* flag to indicate padding with zero */
Boolean sign_on; /* flag to indicate '+' */
Boolean space_on; /* flag to indicate ' ' */
Boolean pound_on; /* flag to indicate '#' */
Boolean neg; /* flag to indicate a negative number */
register char *format; /* pointer to format string */
register char *argument; /* pointer to arguments for fmt string */
#ifdef _FORMAT_FP
#define outputx _output
#else
register void (*outputx)() = _output;
#endif
register char c; /* current character under inspection */
unsigned long value; /* for conversion of numerics */
register int length; /* length of argument to be inserted */
register int width; /* field width as specified by format */
register int numleft; /* # of characters left to print */
register int precision; /* precision of number to be converted */
#ifdef _FORMAT_FP_
/* floating point junk */
double tempdouble;
char floatbuffer[256];
char tempbuffer[256];
register char *bufptr;
register char *tbufptr;
#endif _FORMAT_FP_
/* set up the direct error return to caller */
if (setjmp(env)) return (EOF);
_num_count = 0; /* set # characters transmitted to zero */
format = *fmt++; /* set "format" to the format string */
/* "fmt" is left pointing to first argument */
argument = (char *) fmt; /* set "argument" to point to first argument */
/* if variable, indirect through first argument to find real arg list */
if (var_stuff) argument = (char *)(*(long *)argument);
while (c = *format++) /* scan format string until NULL */
{
if (c == '%')
{
left_justify = false;
zero_fill = false;
sign_on = false;
space_on = false;
pound_on = false;
neg = false;
flagloop:
switch(c = *format++)
{
case '-': left_justify = true;
zero_fill = false;
goto flagloop;
case '0': zero_fill = (!left_justify);
goto flagloop;
case '+': sign_on = true;
space_on = false;
goto flagloop;
case ' ': space_on = (!sign_on);
goto flagloop;
case '#': pound_on = true;
goto flagloop;
case '*': numleft = width = *(int *)argument;
argument += sizeof(int);
goto getdot;
default: format--; /* need to back up if unrecognized */
break;
}
/* compute total field width */
{char *tempformat = format; /* let format be register */
numleft = width = (isdigit(*format)) ? _std_decode(&tempformat) : 0;
format = tempformat;
}
getdot:
/* compute precision within total field width */
precision = 0;
if (do_precision = (*format == '.'))
{
if (*++format == '*')
{
precision = *(int *)argument;
argument += sizeof(int);
format++;
}
else
{char *tempformat = format; /* let format be register */
precision = _std_decode(&tempformat);
format = tempformat;
}
}
if (*format == '%')
{
if (left_justify)
{
(*outputx)('%');
padd(BLANKS,width-1);
}
else
{
padd(zero_fill,width-1);
(*outputx)('%');
}
format++;
continue;
}
/* check if long integer is required */
if (its_a_long = ((c = (*format++)) == 'l'))
c = *format++;
/* check for short %h.. */
if (c == 'h') c = *format++;
/* note that "format" is left ready for the next pass */
switch (c)
{
case 'd':
{register long svaluel; /* for conversion of signed long */
register int svaluei; /* for conversion of signed int */
if (its_a_long)
{
svaluel = *(long *)argument;
argument += sizeof(long);
value = (unsigned long)svaluel;
if (svaluel < 0) goto negate;
}
else
{
svaluei = *(int *)argument;
argument += sizeof(int);
value = (unsigned long)svaluei;
if (svaluei < 0)
{
negate: neg = true;
value = -value;
}
}
/* fall into case 'u' */
}
case 'u':
base = 10;
goto evaluate;
case 'o':
base = 8;
goto evaluate;
case 'x': _hex_case = 'a'-10;
goto sethex;
case 'X':
_hex_case = 'A'-10;
sethex:
base = 16;
evaluate:
/* get the target value in a long,
readjust the argument pointer */
if (c != 'd')
{
if (its_a_long)
{
value = *(unsigned long *)argument;
argument += sizeof (unsigned long);
}
else
{
value = *(unsigned *)argument;
argument += sizeof (unsigned);
}
}
/* get the length of the output string */
length = length_of_number(value);
/* now figure out the positioning */
if (zero_fill)
{
if (((neg)||(sign_on)||
(space_on)) && (c =='d'))
{
if (neg)
{
(*outputx)('-');
}
else
(*outputx)((sign_on ? '+' : ' '));
numleft--; /* room for sign */
}
if (((c=='o')||(c == 'X')||(c == 'x'))&&(pound_on))
{
(*outputx)('0');
numleft--;
if (c == 'X') { (*outputx)('X');numleft--; }
if (c == 'x') { (*outputx)('x');numleft--; }
}
padd(zero_fill, numleft - max(length,precision));
if (precision > length)
padd(zero_fill,precision-length);
output_number(value);
}
if (left_justify)
{
if (c =='d')
{
if (neg)
{
(*outputx)('-');
numleft--;
}
else
if((sign_on)||(space_on))
{
(*outputx)((sign_on ? '+' : ' '));
numleft--;
}
}
if (((c=='o')||(c == 'X')||(c == 'x'))&&(pound_on))
{
(*outputx)('0');
numleft--;
if (c == 'X') { (*outputx)('X');numleft--; }
if (c == 'x') { (*outputx)('x');numleft--; }
}
padd(true,precision-length);
if(precision-length>0) numleft -= (precision-length);
output_number(value);
numleft -= length;
padd(BLANKS, numleft);
}
if ((!zero_fill)&&(!left_justify))
{
if (((neg)||(space_on)||
(sign_on)) && (c == 'd'))
{
padd(zero_fill, width - max(length,precision)-1);
if (neg)
{
(*outputx)('-');
}
else
(*outputx)(((sign_on)?'+':' '));
padd(true,precision-length);
output_number(value);
} /* normal decimal output with sign */
else
if ((pound_on)&&((c == 'o')||(c == 'X')||(c == 'x')))
{
numleft = 2;
if ( c == 'o') numleft = 1;
padd(zero_fill, width - max(length,precision)-numleft);
(*outputx)('0');
if (c == 'X') (*outputx)('X');
if (c == 'x') (*outputx)('x');
padd(true,precision-length);
output_number(value);
}
else
{
padd(zero_fill, width - max(precision,length));
padd(!zero_fill,precision-length);
output_number(value);
}
} /* ((!zero_fill)&&(!left_justify)) */
break;
case 'c':
/* get the character and readjust the pointer
note that a char on the stack is expanded to int */
if (left_justify)
{
(*outputx)(*(int *) argument);
padd(BLANKS, width - 1);
}
else
{
padd(zero_fill, width - 1);
(*outputx)(*(int *) argument);
}
argument += sizeof (int);
break;
case 'p':
{register char *char_ptr;
/* get the length of the string */
char_ptr = *((char **)argument);
length = *char_ptr++;
argument += sizeof(char_ptr);
/* check for truncation */
if (do_precision)
if (length > precision) length = precision;
/* set width to padding width */
if ((width -= length) < 0) width = 0;
if (left_justify)
{
while (--length >= 0)
(*outputx)(*char_ptr++);
padd(BLANKS, width);
}
else
{
padd(zero_fill, width);
while (--length >= 0)
(*outputx)(*char_ptr++);
}
break;
}
case 's':
{register char *char_ptr;
/* get the length of the string */
length = len_of_str(char_ptr = *((char **) argument));
argument += sizeof(char_ptr);
/* check for truncation */
if (do_precision)
if (length > precision) length = precision;
/* set width to padding width */
if ((width -= length) < 0) width = 0;
if (left_justify)
{
while (--length >= 0)
(*outputx)(*char_ptr++);
padd(BLANKS, width);
}
else
{
padd(zero_fill, width);
while (--length >= 0)
(*outputx)(*char_ptr++);
}
break;
}
#ifdef _FORMAT_FP_
case 'f':
tempdouble = *((double *)argument); /* consume argument */
argument += sizeof(double);
/* default precision is 6 */
if (do_precision == false) precision = 6;
regf:
bufptr = cvtf2string(&tempdouble, 1, precision, floatbuffer);
nregf:
tbufptr = tempbuffer;
if ((tempdouble >= 0)&&((space_on)||
(sign_on)))
*tbufptr++ = (sign_on ? '+' : ' ');
while(c = *(bufptr++))
*tbufptr++ = c;
if ((pound_on)&&(precision == 0))
*tbufptr++ = '.';
*tbufptr = '\0';
dopadding(tempbuffer,left_justify,zero_fill,width);
break;
case 'e':
case 'E':
tempdouble = *((double *)argument); /* consume argument */
argument += sizeof(double);
/* default precision is 6 */
if (do_precision == false) precision = 6;
bufptr = cvtf2string(&tempdouble, 0, precision+1, floatbuffer);
rege:
tbufptr = tempbuffer;
if ((tempdouble >= 0)&&(sign_on)) /* if + then make prefix */
*bufptr = '+';
if ((space_on == false)&&(*bufptr == ' '))
bufptr++; /* if leading space and no flag fix it */
while(*(bufptr))
{
if (*bufptr == 'e') /* is this the e char? */
{
neg = true;
*bufptr = c; /* convert if if necessary */
if ((pound_on)&&(precision == 0))
*tbufptr++ = '.';
}
if ((*(bufptr-2) == c)&&(neg)) /* should we pad the exp */
tbufptr = check_for_three(bufptr,tbufptr);
if (*bufptr != '@')
*tbufptr++ = *bufptr++;
}
*tbufptr = '\0';
dopadding(tempbuffer,left_justify,zero_fill,width);
break;
case 'g':
case 'G':
tempdouble = *((double *)argument); /* consume argument */
argument += sizeof(double);
if (precision < 1) precision = 1;
if (do_precision == false) precision = 6;
/* convert to e form */
bufptr = cvtf2string(&tempdouble, 0, precision, floatbuffer);
/* look at char after 'e' */
while (*bufptr++ != 'e')
;
neg = (*bufptr++ == '-'); /* is it a negative exponent */
tbufptr = bufptr;
/* get size of exponent */
length = _std_decode(&tbufptr);
if (neg) length *= -1;
if (pound_on) /* if # flag is on and ... */
{
if ((length<=precision)&&(length>=-4))
goto regf; /* do regular f routine */
else
{
/* this gives precision-1 digits after
. instead of precision */
bufptr = (char *)floatbuffer;
c -= 2;
goto rege; /* do regular e routine */
}
}
if ((length<=precision)&&(length>=-4))
{register Boolean strip_it = false;
/* convert to f format */
bufptr = cvtf2string(&tempdouble, 1, precision-length, floatbuffer);
/* don't strip string unless therre is a . */
while (*bufptr)
{
strip_it = strip_it?true:(*bufptr == '.');
bufptr++;
}
bufptr -= 1; /* was 2 */
/* strip trailing zeros */
if (strip_it)
{
while (*bufptr == '0') bufptr--;
if (*bufptr == '.') bufptr--;
*(++bufptr) = '\0';
}
bufptr = (char *)floatbuffer;
/* now test for \0 or -\0 and adjust */
if ((*bufptr == '\0') ||
((*bufptr=='-')&&(*(bufptr+1)=='\0')))
{
*bufptr = '0';
*(bufptr+1) = '\0';
}
goto nregf;
break;
}
/* back up before the e and strip trailing zeros */
bufptr -= 3;
while ((*bufptr == '0')||(*bufptr == '.'))
*(bufptr--) = '@';
/* bufptr = (char *)floatbuffer;
c -= 2; / convert form g to e, G to E /
goto rege;
*/
if ((space_on == false)&&(floatbuffer[0] == ' '))
floatbuffer[0] = '@';
if ((tempdouble>=0) && (sign_on)) floatbuffer[0] = '+';
tbufptr = (char *)tempbuffer;
bufptr = (char *)floatbuffer;
while (*bufptr != 'e')
if (*bufptr != '@')
*tbufptr++ = *bufptr++;
else
bufptr++;
*bufptr = c-2; /* convert G to E, g to e */
*tbufptr++ = *bufptr++;
*tbufptr++ = *bufptr++;
tbufptr = check_for_three(bufptr,tbufptr);
while (*bufptr)
*tbufptr++ = *bufptr++;
*tbufptr = '\0';
dopadding(tempbuffer,left_justify,zero_fill,width);
break;
#endif _FORMAT_FP_
default:
/* input garbled, bark: arf arf */
return (EOF);
} /* end of switch */
} /* end of if */
else
/* copy it directly to output */
(*outputx)(c);
} /* end of while */
return(_num_count);
(void) std_ver(); /* make the linker drag it in */
}